#!/usr/bin/python
###############################################################################
#Programmers:                                                                 #
#             Danil Dotsenko, dd@accentsolution.com                           #
#Date: 07/11/2006                                                             #
#Filename: localConf.py                                                       #
#Version: 0.1                                                                 #
#                                                                             #
#                                                                             #
#       Copyleft 2006 Danil Dotsenko                                          #
#                                                                             #
#    This program is free software; you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation; either version 2 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program; if not, write to the Free Software              #
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA #
#                                                                             #
###############################################################################

# Essentially, this is a wrapper for ConfigParser module.
# By itself, ConfigParser is not nice - gives too many error messages and requires too much checking.
# This wrapper navigates all calls around potential error conditions by doing the checks by itself and giving "None" instead of Error states.
# In addition to that, new functions try to accomodate the calls with missing data by doing the searches for them.
# In the end, this should produce very clean (karamba-like) call responces in the actual application.
#
# Warning! The set function intentionally writes in unicode. 

'''
Usefull Tips:
	- There is no "multi-homing" i.e. only one config file name is considered main at a time.
	- All errors and issues are hidden. If data is requested from non-existent section - value is None, not an Error
	- This module takes responsibility for all needed checks (file / section / option existense).
	 All user should care about is how to:
	 load(fileName, [setNameAsDefault=True/False])
	 get(section, option)
	 set(section, option, value)
	 save([fileName]) note, fileName is optional because the the one loaded may be set as default.
	- One unusual function in the mix is delayed_action([action], [argument])
	 When passed WITH arguments (see the function for allowed arguments),
	 it sets an action to be performed later.
	 When passed WITHOUT arguments, it performs the LAST requested action.
	 This is very usefull for situations when a long list of IF's set some options,
	 but you want to delay the save until you are done with all of them.
'''

import os.path
import ConfigParser

class cfgObject:
	def __init__(self, fileName = None):
		"""__init__([fileName]) Creates the Config object. Without a file name, the config is initialized empty. With a file name, the config is populated by the settings from file."""
		self.cfgObject = ConfigParser.ConfigParser()
		self.fileName = None
		self.delayed = None
		if fileName:
			if os.path.exists(fileName):
				self.cfgObject.read(fileName)
				self.fileName = fileName

	def load(self, fileName, fileNamePermanent=False):
		"""set_file(fileName, [setNamePermanent=True/False]) This should add the configs from a file to the current instance. If True is used, this now becomes the main config file."""
		if fileName: # safeguarding for None as fileName
			if os.path.exists(fileName):
				if self.cfgObject.read(fileName):
					if fileNamePermanent or self.fileName == None:
						self.fileName = fileName
					return 1
		return 0

	def findSection(self,option):
		"""findSection(option) Finds a section name for the 1st occurance of option name.
		If section name is not given, lets be nice and return contents of 1st occurance of option anywhere."""
		allSections = self.cfgObject.sections()
		i = 0
		section = None
		while i < allSections.__len__() and section == None:
			allOptions = self.cfgObject.options(allSections[i])
			if allOptions.__contains__(option):
				section = allSections[i]
			i = i + 1
		return section

	def exists(self,section=None,option=None):
		"""exists([section],[option])
		Confirms existense of section or option. You can check both, or each individually.
		If only option is given (i.e. exists(None, optionName)) than we search for that option among all sections."""
		answer = False
		if section:
			if self.cfgObject.has_section(section):
				answer = True
				if option:
					if self.cfgObject.has_option(section,option) == False:
						answer = False
		elif option:
			if self.findSection(option):
				answer = True
		else: 
			pass
		return answer
	
	def get(self,section,option):
		"""get(section, option) returns either the contents of option, or None. Use findSection(option) if you don't know section."""
		answer = None
		if self.exists(section,option):
				answer = self.cfgObject.get(section,option)
		return answer

	def set(self,section,option,value):
		"""set(section,option,value) Unlike ConfigParser's set, this function will create section and option if doesn'texist."""
		# ConfigParser gives error if on set a section does not exist. 
		# We will be nice and create the section silently.
		# Also, ConfigParser does not check what is given to it by Value. It brakes on get(value) if not string.
		if self.cfgObject.has_section(section) == False:
			self.cfgObject.add_section(section)
		self.cfgObject.set(section,option,unicode(value))
	
	def delete(self,section,option=None):
		"""delete(section, [option]) removes the entry. If option is given, option is removed. Else, section is removed."""
		if option:
			if self.cfgObject.has_section(section):
				self.cfgObject.remove_option(section, option)
		else:
			self.cfgObject.remove_section(section)
	
	def ls(self, section=None):
		"""ls([section name]) Returns a list var with the contents of the whole config file. If section is given only lists the options in the section."""
		if self.exists(section):
			allSections = [section]
		else:
			allSections = self.cfgObject.sections()
		i = 0
		allOptions = []
		for i in range(allSections.__len__()):
			tmpString = self.cfgObject.options(allSections[i])
			allOptions.append(allSections[i]+"="+",".join(tmpString))
		return allOptions
	
	def save(self, fileName=None, fileNamePermanent = False):
		"""save([fileName], [makeNamePermanent=False/True]) Saves into the requested file, else saves into path in self.fileName"""
		if (fileNamePermanent and fileName) or self.fileName==None: # this is just about setting the default.
			self.fileName = fileName # note, this may still result in None.
		if fileName == None: # fileName was not given, we need to use the default fileName
			fileName = self.fileName
		if fileName: # At this point, if self.fileName, or fileName had value, we are ok. If both had None - return False
			filePtr = open(fileName,'w')
			self.cfgObject.write(filePtr)
			filePtr.close()
			return True
		else:
			return False
	
	def delayed_action(self, action=None, argument=None):
		"""delayed_action([save/load/reload],[agrument]) This is a trick used to delay multiple saves or reloads followed after repeated usage of cfgObject. Call with no arguments to perform the last requested action"""
		if action == None:
			if self.delayed[0] == "save":
				self.save(self.delayed[1])
			elif self.delayed[0] == "load":
				self.load(self.delayed[1])
			elif self.delayed[0] == "reload":
				self.load()
			self.delayed = None
		else:
			self.delayed = [action, argument]